package top.hekun.study.core;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.StaticInitMerger;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.util.CheckClassAdapter;
import top.hekun.study.core.util.ClassFileUtil;
import top.hekun.study.core.visitor.complex.*;

import java.io.File;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;

/**
 * 〈ClassReader 读取文件 并复杂修改文件〉 类修改复杂演示
 *
 * @author: Sakura
 * @date: 2023/5/7
 */
public class ClassComplexModifyClassDemo {
    // 是否开启检查
    private static final boolean CHECK = false;
    // 是否开启 进行中检查
    private static final boolean HAVE_IN_HAND_CHECK = false;
    // 是否开启结束检查
    private static final boolean END_CHECK = false;
    // 是否伪造错误
    public static final boolean ERROR_CODE = false;


    public static void main(String[] args) {

        byte[] helloWords = handle("HelloWord");
        writeClassFileCore(helloWords, getClassFile("HelloWordComplexModifyClass"));
        writeClassFileCore(handle("HelloWordComplexRun"), getClassFile("HelloWordComplexRun"));
        byte[] newErrorHelloWords = mergeClass(helloWords, handle("HelloWordTwo"));
        writeClassFileCore(newErrorHelloWords,getClassFile("newErrorHelloWords"));

        byte[] newHelloWords = removeDuplicateStaticInitMethod(newErrorHelloWords);
        writeClassFileCore(newHelloWords,getClassFile("newHelloWords"));

    }
    // 合并两个类
    private static byte[]  mergeClass(byte[] bytes1,byte[] bytes2){
        int apiVersion = Opcodes.ASM9;
        //（1）构建ClassReader
        ClassReader classReader = new ClassReader(bytes1);
        ClassVisitor classVisitor;
        //（2）构建ClassWriter
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        classVisitor = classWriter;
        ClassNode classNode = getClassNode(bytes2);
        classVisitor = new MyClassVisitor2(apiVersion, classVisitor,classNode);
        classVisitor = new MyClassVisitor3(apiVersion, classVisitor,classNode);
        classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES | ClassReader.SKIP_DEBUG);
        return  classWriter.toByteArray();
    }
    // 清除重复的 静态方好体 并设置 serialVersionUID
    public static byte[] removeDuplicateStaticInitMethod(byte[] bytes) {
        int apiVersion = Opcodes.ASM9;
        //（1）构建ClassReader
        ClassReader cr = new ClassReader(bytes);
        ClassVisitor classVisitor;
        //（2）构建ClassWriter
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        classVisitor = classWriter;
        //（3）串连ClassVisitor
        classVisitor = new StaticInitMerger("class_init$", classVisitor);

        classVisitor = new MyClassVisitor4(apiVersion, classVisitor);
        //（4）结合ClassReader和ClassVisitor
        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
        cr.accept(classVisitor, parsingOptions);

        //（5）生成byte[]
        return classWriter.toByteArray();
    }
    /**
     * tree API 中的方法, 将方法解析成树结构
     * @param bytes
     * @return
     */
    public static ClassNode getClassNode(byte[] bytes) {
        ClassReader cr = new ClassReader(bytes);
        ClassNode cn = new ClassNode();
        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
        cr.accept(cn, parsingOptions);
        return cn;
    }

    private static byte[] handle(String className) {
        byte[] classBytes = ClassFileUtil.getClassFileByteToClassName(className);
        ClassReader classReader = new ClassReader(classBytes);
        System.out.println("access > " + classReader.getAccess());
        System.out.println("className > " + classReader.getClassName());
        System.out.println("classSuperName > " + classReader.getSuperName());
        System.out.println("Interfaces > " + Arrays.toString(classReader.getInterfaces()));
        System.out.println();

        ClassVisitor classVisitor;
        // 类编写器
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        // 方法检查 在 MyClassVisitor6 中查到错误
        classVisitor = classWriter;
        if (CHECK && HAVE_IN_HAND_CHECK) {
            classVisitor = new CheckClassAdapter(classWriter);
        }
        int apiVersion = Opcodes.ASM9;
        // 更改类的版本
        classVisitor = new MyClassVisitor1(apiVersion, classVisitor);

        // 运行的时候 方法倒序调用 6>5>4>3>2>1
        classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES | ClassReader.SKIP_DEBUG);
        byte[] classByte = classWriter.toByteArray();
        if (CHECK && END_CHECK) {
            CheckClassAdapter.verify(new ClassReader(classByte), false, new PrintWriter(System.out));
        }
      return classByte;
    }

    private static File getClassFile(String className) {
        File file = new File(ClassFileUtil.getClassFileModelPath(), className + ".class");
        return file;
    }

    private static void writeClassFileCore(byte[] core, File classFilePath) {
        ClassFileUtil.classFileCoreWrite(core, classFilePath);
    }

}
